﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AddIn.EA.Core.Common;
using AddIn.EA.Framework;
using System.Configuration;
using System.Threading;

namespace AddIn.EA.Architecture.Presentation
{
    [AddInMenu("Create Standard Project")]
    public partial class CreateStandardProject : BaseAddInArchitecture
    {
        #region Local Objects
        private List<ComponentLogic.StandardProject.BusinessEntityStandardProject> entities;
        private ComponentLogic.StandardProject.StandardProject standardProjectLogic;
        private ComponentLogic.ElementModel.Package viewModel;
        private TreeNode baseViewNode;
        private List<TreeViewState> tvVisualizationState;
        private bool shouldSaveState = true;
        #endregion

        #region Start Form
        public CreateStandardProject()
        {
            InitializeComponent();
            base.LoadAddInControls += new EventHandler(CreateStandardProject_LoadAddInControls);
            base.UnloadAddInControls += new EventHandler(CreateStandardProject_UnloadAddInControls);

            entities = new List<AddIn.EA.Architecture.ComponentLogic.StandardProject.BusinessEntityStandardProject>();
            standardProjectLogic = new AddIn.EA.Architecture.ComponentLogic.StandardProject.StandardProject(Repository);
            viewModel = new AddIn.EA.Architecture.ComponentLogic.ElementModel.Package("Root");
        }
        void CreateStandardProject_LoadAddInControls(object sender, EventArgs e)
        {
            StartForm();
        }
        private void CreateStandardProject_Load(object sender, EventArgs e)
        {
            StartForm();
        }
        private void StartForm()
        {
            try
            {
                new Thread(new ThreadStart(LoadTemplate)).Start();

                tvEAStructure.Nodes[0].Nodes.Clear();
                AddIn.EA.Framework.Common.IElement element = null;
                TreeNode baseNode = null;
                baseViewNode = tvEAStructure.Nodes[0];

                if (Repository != null)
                {
                    element = Repository.GetSelectedObject();
                    baseNode = BuildBaseNode(element);
                    if (baseNode != null)
                    {
                        tvEAStructure.Nodes[0].Nodes.Add(GetTopNode(baseNode));
                        tvEAStructure.SelectedNode = baseNode;
                        baseViewNode = baseNode;
                    }
                }

                tvEAStructure.Nodes[0].ExpandAll();
            }
            catch (Exception ex)
            {
                HandleException("Inicializar o AddIn \"CreateStandardProjects\"", ex);
            }
        }
        private void LoadTemplate()
        {
            try
            {
                while (!this.IsHandleCreated)
                    Thread.Sleep(300);

                BeginInvoke(new Action(delegate()
                {
                    splitContainer1.Visible = false;
                    pnlLoadingTemplate.Visible = true;
                    cmdApplyChanges.Enabled = false;
                }));

                RepositoryManager rep = ComponentLogic.StandardProject.StandardProject.TemplateRepository;
                if (rep == null)
                    throw new Exception("Não foi possível carregar o template");

                BeginInvoke(new Action(delegate()
                {
                    cmdApplyChanges.Enabled = true;
                }));

            }
            catch (Exception ex)
            {
                HandleException("Carregar o template do gerador.", ex);
            }
            finally
            {
                BeginInvoke(new Action(delegate()
                {
                    pnlLoadingTemplate.Visible = false;
                    splitContainer1.Visible = true;
                }));
            }
        }
        #endregion

        #region End Form
        void CreateStandardProject_UnloadAddInControls(object sender, EventArgs e)
        {
        }
        private void CreateStandardProject_FormClosing(object sender, FormClosingEventArgs e)
        {
        }
        #endregion

        #region Events
        private void cmdNewEntity_Click(object sender, EventArgs e)
        {
            CreateNewEntity();
        }
        private void cmdEntityMethods_Click(object sender, EventArgs e)
        {
            OpenEntityMethods();
        }
        private void cmdEdit_Click(object sender, EventArgs e)
        {
            EditSelectedEntity();
        }
        private void cmdDelete_Click(object sender, EventArgs e)
        {
            DeleteSelectedEntity();
        }
        private void cmdApplyChanges_Click(object sender, EventArgs e)
        {
            ApplyEntityToView();
        }
        private void cmdCleanView_Click(object sender, EventArgs e)
        {
            CleanView();
        }
        private void cmdAplyEAModel_Click(object sender, EventArgs e)
        {
            AplyModelToEA();
        }
        private void lstEntities_DoubleClick(object sender, EventArgs e)
        {
            EditSelectedEntity();
        }
        private void lstEntities_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
                DeleteSelectedEntity();
        }
        private void ctxNewEntity_Click(object sender, EventArgs e)
        {
            CreateNewEntity();
        }
        private void ctxEntityMethods_Click(object sender, EventArgs e)
        {
            OpenEntityMethods();
        }
        private void ctxEditEntity_Click(object sender, EventArgs e)
        {
            EditSelectedEntity();
        }
        private void ctxDeleteEntity_Click(object sender, EventArgs e)
        {
            DeleteSelectedEntity();
        }
        private void lstEntities_Resize(object sender, EventArgs e)
        {
            lstEntities.Columns[0].Width = lstEntities.Width - lstEntities.Columns[1].Width - 23;
        }
        private void tvEAStructure_AfterExpand(object sender, TreeViewEventArgs e)
        {
            SaveTreeViewState();
        }
        private void tvEAStructure_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            SaveTreeViewState();
        }
        private void recolherTudoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tvEAStructure.SelectedNode.Collapse(false);
        }
        private void expandirTudoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tvEAStructure.SelectedNode.ExpandAll();
        }
        private void tvEAStructure_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
                tvEAStructure.SelectedNode = e.Node;
        }
        private void toolSave_Click(object sender, EventArgs e)
        {
            SaveSelectedEntities();
        }
        private void toolOpen_Click(object sender, EventArgs e)
        {
            OpenSavedEntities();
        }
        private void ctxApplyToVisualization_Click(object sender, EventArgs e)
        {
            ApplyEntityToView();
        }
        private void ctxSave_Click(object sender, EventArgs e)
        {
            SaveSelectedEntities();
        }
        private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
        {
            HandleContextMenuLstEntitiesState();
        }
        #endregion

        #region Methods
        private TreeNode BuildBaseNode(AddIn.EA.Framework.Common.IElement element)
        {
            TreeNode resultNode = null;
            try
            {
                if (element != null && element is Package)
                {
                    TreeNode parentNode = null;

                    TreeNode itemNode = new TreeNode(element.Name);
                    itemNode.Tag = element;

                    if (element.Parent != null)
                    {
                        parentNode = BuildBaseNode(element.Parent);
                        parentNode.Nodes.Add(itemNode);
                    }

                    resultNode = itemNode;
                }
            }
            catch (Exception ex)
            {
                HandleException("Carregar os nodes iniciais do EA", ex);
            }
            return resultNode;
        }
        private TreeNode GetTopNode(TreeNode node)
        {
            TreeNode returnNode = null;

            try
            {
                if (node.Parent != null)
                    returnNode = GetTopNode(node.Parent);
                else
                    returnNode = node;
            }
            catch (Exception ex)
            {
                HandleException("Obter o node principal", ex);
            }

            return returnNode;
        }
        private void LoadEntities()
        {
            try
            {
                lstEntities.Items.Clear();
                if (entities != null && entities.Count > 0)
                {
                    foreach (ComponentLogic.StandardProject.BusinessEntityStandardProject itemEntity in entities)
                    {
                        ListViewItem listItem = new ListViewItem(itemEntity.Name);
                        listItem.SubItems.Add(itemEntity.NamePlural);
                        lstEntities.Items.Add(listItem);
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException("Carregar a lista de Entidades", ex);
            }
        }
        private void CreateNewEntity()
        {
            try
            {
                CreateStandardProjectBusinessEntity wd = new CreateStandardProjectBusinessEntity(null, Repository, entities);
                wd.ShowDialog(this);
                if (wd.Tag != null)
                {
                    entities.Add(wd.Tag as ComponentLogic.StandardProject.BusinessEntityStandardProject);
                    LoadEntities();
                }
            }
            catch (Exception ex)
            {
                HandleException("Iniciar a abertura da tela para cadastrar uma nova entidade.", ex);
            }
        }
        private void OpenEntityMethods()
        {
            try
            {
                if (lstEntities.SelectedItems.Count == 1)
                {
                    ComponentLogic.StandardProject.BusinessEntityStandardProject entity = entities.Find(it => it.Name.CompareTo(lstEntities.SelectedItems[0].SubItems[0].Text) == 0);
                    if (entity != null)
                    {
                        CreateStandardProjectMethods wd = new CreateStandardProjectMethods(entity.Methods, entity, Repository, entities);
                        wd.ShowDialog(this);
                        if (wd.Tag != null)
                            entity.Methods = wd.Tag as List<ComponentLogic.StandardProject.MethodStandardProject>;
                    }
                }
                else
                    MessageBox.Show("Selecione uma mensagem para editar os métodos.", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Iniciar a abertura da tela para edição dos métodos da entidade.", ex);
            }
        }
        private void EditSelectedEntity()
        {
            try
            {
                if (lstEntities.SelectedItems.Count == 1)
                {
                    ComponentLogic.StandardProject.BusinessEntityStandardProject entity = entities.Find(it => it.Name.CompareTo(lstEntities.SelectedItems[0].SubItems[0].Text) == 0);
                    if (entity != null)
                    {
                        CreateStandardProjectBusinessEntity wd = new CreateStandardProjectBusinessEntity(entity, Repository, entities);
                        wd.ShowDialog(this);
                        if (wd.Tag != null)
                        {
                            entity = wd.Tag as ComponentLogic.StandardProject.BusinessEntityStandardProject;
                            LoadEntities();
                        }
                    }
                }
                else
                    MessageBox.Show("Selecione uma entidade para editar!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Iniciar edição de uma entidade.", ex);
            }
        }
        private void DeleteSelectedEntity()
        {
            try
            {
                if (lstEntities.SelectedItems.Count > 0)
                {
                    if (MessageBox.Show("Deseja realmente excluir as entidades selecionadas? Todas entidades não aplicadas à visualização serão perdidas!", "Questão", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        while (lstEntities.SelectedItems.Count > 0)
                        {
                            entities.RemoveAll(it => it.Name.CompareTo(lstEntities.SelectedItems[0].SubItems[0].Text) == 0);
                            lstEntities.SelectedItems[0].Remove();
                        }
                    }
                }
                else
                    MessageBox.Show("Selecione ao menos uma entidade para excluir!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Excluir uma entidade.", ex);
            }
        }
        private void ApplyEntityToView()
        {
            try
            {
                if (lstEntities.SelectedItems.Count > 0)
                {
                    viewModel.Packages.Clear();
                    foreach (ListViewItem listItem in lstEntities.SelectedItems)
                        standardProjectLogic.ApplyEntityModelToPackageStructure(entities.Find(it => it.Name.CompareTo(listItem.SubItems[0].Text) == 0), viewModel);
                    shouldSaveState = false;
                    LoadRepositoryView();
                    shouldSaveState = true;
                }
                else
                    MessageBox.Show("Selecione ao menos uma entidade para aplicar à visualiação!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                shouldSaveState = true;
                HandleException("Aplicar entidades à visualização do modelo.", ex);
            }
        }
        private void LoadRepositoryView()
        {
            try
            {
                baseViewNode.Nodes.Clear();
                BindTreeViewPackage(viewModel.Packages, baseViewNode);
                RestoreTreeViewState();
            }
            catch (Exception ex)
            {
                HandleException("Carregar a visualização do modelo.", ex);
            }
        }
        private void BindTreeViewPackage(List<ComponentLogic.ElementModel.Package> packageList, TreeNode node)
        {
            foreach (ComponentLogic.ElementModel.Package itemPackage in packageList)
            {
                TreeNode packageNode = new TreeNode(itemPackage.Name, 0, 1);
                packageNode.Name = itemPackage.Name;

                if (itemPackage.Packages.Count > 0)
                    BindTreeViewPackage(itemPackage.Packages, packageNode);

                if (itemPackage.Elements.Count > 0)
                    BindTreeViewPackageElements(itemPackage.Elements, packageNode);

                node.Nodes.Add(packageNode);
            }
        }
        private void BindTreeViewPackageElements(List<ComponentLogic.ElementModel.Element> elementList, TreeNode packageNode)
        {
            foreach (ComponentLogic.ElementModel.Element itemElement in elementList)
            {
                TreeNode nodeElement = new TreeNode((!String.IsNullOrEmpty(itemElement.Stereotype) ?
                                                     "<" + itemElement.Stereotype + "> " :
                                                     String.Empty) +
                                                     itemElement.Name,
                                                     itemElement.Type == AddIn.EA.Architecture.ComponentLogic.ElementModel.ElementType.Class ? 3 : 5,
                                                     itemElement.Type == AddIn.EA.Architecture.ComponentLogic.ElementModel.ElementType.Class ? 3 : 5);
                nodeElement.Name = itemElement.Name;

                if (itemElement.Elements != null && itemElement.Elements.Count > 0)
                    BindTreeViewPackageElements(itemElement.Elements, nodeElement);

                if (itemElement.Attributes != null && itemElement.Attributes.Count > 0)
                    BindTreeViewPackageElementAttributes(itemElement.Attributes, nodeElement);

                if (itemElement.Methods != null && itemElement.Methods.Count > 0)
                    BindTreeViewPackageElementMethods(itemElement.Methods, nodeElement);

                packageNode.Nodes.Add(nodeElement);
            }
        }
        private void BindTreeViewPackageElementAttributes(List<AddIn.EA.Architecture.ComponentLogic.ElementModel.Attribute> attributeList, TreeNode nodeElement)
        {
            foreach (ComponentLogic.ElementModel.Attribute itemAttribute in attributeList)
            {
                TreeNode nodeAttribute = new TreeNode(itemAttribute.Name + ":" + itemAttribute.Type, 4, 4);
                nodeAttribute.Name = itemAttribute.Name;
                nodeElement.Nodes.Add(nodeAttribute);
            }
        }
        private void BindTreeViewPackageElementMethods(List<ComponentLogic.ElementModel.Method> methodList, TreeNode nodeElement)
        {
            foreach (ComponentLogic.ElementModel.Method itemMethod in methodList)
            {
                TreeNode nodeMethod = new TreeNode(itemMethod.Name + "(" + itemMethod.Parameters + ") : " + itemMethod.Return, 2, 2);
                nodeMethod.Name = itemMethod.Name + itemMethod.Parameters;
                nodeElement.Nodes.Add(nodeMethod);
            }
        }
        private void CleanView()
        {
            try
            {
                viewModel = new AddIn.EA.Architecture.ComponentLogic.ElementModel.Package("Root");
                LoadRepositoryView();
            }
            catch (Exception ex)
            {
                HandleException("Limpar a visualização do modelo.", ex);
            }
        }
        private void AplyModelToEA()
        {
            try
            {
                standardProjectLogic.ApplyPackageStructureToEA(viewModel, Repository.GetSelectedObject() as Framework.Package);
                MessageBox.Show("Modelo aplicado ao EA com sucesso!", "Confirmação", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                HandleException("Aplicar o modelo ao EA.", ex);
            }
        }
        private void SaveTreeViewState()
        {
            try
            {
                if (shouldSaveState)
                {
                    if (tvVisualizationState == null)
                        tvVisualizationState = new List<TreeViewState>();

                    ApplyTvVisualizationState(tvEAStructure.Nodes[0].Nodes);
                }
            }
            catch (Exception ex)
            {
                HandleException("Salvar o estado da TreeView", ex);
            }
        }
        private void ApplyTvVisualizationState(TreeNodeCollection treeNodeCollection)
        {
            foreach (TreeNode itemNode in treeNodeCollection)
            {
                TreeViewState state = tvVisualizationState.Find(it => it.ElementName.CompareTo(itemNode.FullPath) == 0);
                if (state != null)
                    state.ElementState = itemNode.IsExpanded ? TreeViewState.State.Expand : TreeViewState.State.Collapse;
                else
                    tvVisualizationState.Add(new TreeViewState
                    {
                        ElementName = itemNode.FullPath,
                        ElementState = itemNode.IsExpanded ? TreeViewState.State.Expand : TreeViewState.State.Collapse
                    });

                if (itemNode.Nodes.Count > 0)
                    ApplyTvVisualizationState(itemNode.Nodes);
            }
        }
        private void RestoreTreeViewState()
        {
            try
            {
                if (tvVisualizationState != null)
                {
                    ApplyTvVisualizationStateRestore(tvEAStructure.Nodes[0].Nodes);
                }
            }
            catch (Exception ex)
            {
                HandleException("Restaurar o estado da TreeView", ex);
            }
        }
        private void ApplyTvVisualizationStateRestore(TreeNodeCollection treeNodeCollection)
        {
            foreach (TreeNode itemNode in treeNodeCollection)
            {
                TreeViewState state = tvVisualizationState.Find(it => it.ElementName.CompareTo(itemNode.FullPath) == 0);
                if (state != null)
                {
                    switch (state.ElementState)
                    {
                        case TreeViewState.State.Collapse:
                            itemNode.Collapse(true);
                            break;
                        case TreeViewState.State.Expand:
                            itemNode.Expand();
                            break;
                    }
                }
                if (itemNode.Nodes.Count > 0)
                    ApplyTvVisualizationStateRestore(itemNode.Nodes);
            }
        }
        private void SaveSelectedEntities()
        {
            try
            {
                if (lstEntities.SelectedItems.Count > 0)
                {
                    if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
                    {
                        standardProjectLogic.SaveProject(GetSelectedEntities(), saveFileDialog.FileName);
                        MessageBox.Show("Projeto salvo com sucesso!", "Confirmação", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }
                else
                    MessageBox.Show("Selecione ao menos uma entidade para salvar!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Salvar o projeto.", ex);
            }
        }
        private ComponentLogic.StandardProject.Project GetSelectedEntities()
        {
            ComponentLogic.StandardProject.Project project = new AddIn.EA.Architecture.ComponentLogic.StandardProject.Project();
            foreach (ListViewItem listItem in lstEntities.SelectedItems)
                project.Entities.Add(entities.Find(it => it.Name.CompareTo(listItem.SubItems[0].Text) == 0));
            return project;
        }
        private void OpenSavedEntities()
        {
            try
            {
                if (openFileDialog.ShowDialog(this) == DialogResult.OK)
                {
                    ComponentLogic.StandardProject.Project projet = standardProjectLogic.OpenProject(openFileDialog.FileName);

                    if (entities != null && entities.Count > 0)
                    {
                        DialogResult dResult = dResult = MessageBox.Show("Existem entidades em edição.\r\n\r\nDeseja adicionar as entidades abertas?\r\n\r\nSim - Adiciona as entidades abertas à lista em edição.\r\nNão - Substitui as entidades em edição pelas abertas.\r\nCancelar - Cancela a operação.\r\n\r\nObs: Na adição de entidades abertas, entidades com o mesmo nome serão descartadas.", "Questão", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                        switch (dResult)
                        {
                            case DialogResult.Yes: // Add the opened entities.
                                entities.AddRange(projet.Entities.FindAll(it => !entities.Exists(ite => ite.Name.CompareTo(it.Name) == 0)));
                                break;
                            case DialogResult.No: // Replace the existing entities with those opened.
                                entities = projet.Entities;
                                break;
                        }
                    }
                    else
                        entities = projet.Entities;

                    LoadEntities();
                }
            }
            catch (Exception ex)
            {
                HandleException("Carregar um projeto salvo.", ex);
            }
        }
        private void HandleContextMenuLstEntitiesState()
        {
            ctxEntityMethods.Enabled = lstEntities.SelectedItems.Count > 0;
            ctxEditEntity.Enabled = lstEntities.SelectedItems.Count > 0;
            ctxDeleteEntity.Enabled = lstEntities.SelectedItems.Count > 0;
            ctxApplyToVisualization.Enabled = lstEntities.SelectedItems.Count > 0;
            ctxSave.Enabled = lstEntities.SelectedItems.Count > 0;
        }
        #endregion

        #region Overrided Methods
        protected override bool CheckShowForm(AddIn.EA.Framework.Common.IElement selectedElement, AddIn.EA.Core.Common.MenuLocation location)
        {
            return selectedElement is AddIn.EA.Framework.Package &&
                    (location == AddIn.EA.Core.Common.MenuLocation.MainMenu ||
                    location == AddIn.EA.Core.Common.MenuLocation.TreeView);
        }
        #endregion

        #region Inner Classess
        public class TreeViewState
        {
            public enum State
            {
                Collapse,
                Expand
            }

            public String ElementName { get; set; }
            public State ElementState { get; set; }
        }
        #endregion
    }
}
